﻿
#include "EGraphics.h"
#include "EUtil.h"

namespace Base3D
{
	//--------------------------------------------------------------------------

	EBitmap::EBitmap(const EString &filename) : name(filename), pixels(nullptr),
		pitch(0), width(0), height(0), valid(false)
	{
#ifdef GRAPHICS_DGIPLAS
		bitmap = Gdiplus::Bitmap::FromFile(ToEWString(GetPath(filename)).c_str(), true);

		if (bitmap)
		{
			bitmapData	= new Gdiplus::BitmapData;
			Gdiplus::Rect rect(0, 0, getWidth(), getHeight());
			bitmap->LockBits(&rect,Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, bitmapData);

			// pixels[row * bitmapData->Stride / 4 + col]
			pixels = (UINT*)bitmapData->Scan0;
			bitmap->UnlockBits(bitmapData);

			pitch = bitmapData->Stride / 4;

			width = bitmap->GetWidth();
			height = bitmap->GetHeight();

			valid = bitmap != NULL;
		}
#else
		// 材质脚本, 首先获取默认路径
		hBitmap = (HBITMAP)::LoadImage(GHInstance,
                                       gPreference->GetMediaPath(filename).c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);

		if (hBitmap != nullptr)
		{
			bitmapHDC = ::CreateCompatibleDC(nullptr);
			::SelectObject(bitmapHDC, (HGDIOBJ)hBitmap);
			::GetObject(hBitmap,sizeof(BITMAP),&bitmap);

			width	= bitmap.bmWidth;
			height  = bitmap.bmHeight;
			pitch	= bitmap.bmHeight;
			valid	= true;

			pixels = new EColor[width * height];
			for (EInt i = 0; i < getHeight(); i++)
			{
				for (EInt j = 0; j < getWidth(); j++)
				{
					COLORREF color = ::GetPixel(bitmapHDC, i, j);
					pixels[j * width + i] = EColor(GetRValue(color), GetGValue(color), GetBValue(color));
				}
			}
		}
#endif
	}

	//--------------------------------------------------------------------------

	EBitmap::~EBitmap()
	{
#ifdef GRAPHICS_DGIPLAS
		SafeDelete(bitmap);
#else
		DeleteObject(hBitmap);
		DeleteDC(bitmapHDC);
		SafeDeleteArray(pixels);
#endif
	}

	//--------------------------------------------------------------------------

	EColor EBitmap::getPixel(EInt x, EInt y)
	{
#ifdef	GRAPHICS_DGIPLAS
		//Gdiplus::Color color;
		//bitmap->GetPixel(x, y, &color);
		//return EColor(color.GetR(), color.GetG(), color.GetB(), color.GetA());
		EInt color = pixels[x * pitch + y];
		return EColor(color);
#else
		//COLORREF color = ::GetPixel(bitmapHDC, x, y);
		//return EColor(GetRValue(color), GetGValue(color), GetBValue(color));
		//return pixels[y * getWidth() + x];
		return pixels[y * pitch + x];
#endif
	}

	//--------------------------------------------------------------------------

	HINSTANCE GHInstance;

#ifdef	 GRAPHICS_DGIPLAS
	Gdiplus::GdiplusStartupInput	EGraphics::GDiplusStartupInput;
	ULONG_PTR						EGraphics::GDiplusToken;

	Gdiplus::Bitmap					*EGraphics::GCurrentBuffer;
	Gdiplus::Graphics				*EGraphics::GCurrentGraphics;

	Gdiplus::Font					*EGraphics::GFont;

	Gdiplus::BitmapData				*EGraphics::GBitmapData;
	UINT							*EGraphics::GPixels;
#else
	HBITMAP							EGraphics::GBufferedHandle;
	//
	HDC								EGraphics::GBufferedHDC;
	HBRUSH							EGraphics::GBgBrush;
	HPEN							EGraphics::GPen;
	HINSTANCE						EGraphics::GInstance;
	RECT							EGraphics::GBufferSize;
	DIBSECTION						EGraphics::GDIBSection;
	//
	BYTE							*EGraphics::GData;
	EInt							EGraphics::GPitch;
#endif

	EFloat							*EGraphics::GZBuffer;

	//--------------------------------------------------------------------------

	// 初始化绘图系统
	bool EGraphics::initGraphics(HINSTANCE hinstance)
	{
#ifdef	GRAPHICS_DGIPLAS
		Gdiplus::Status state = Gdiplus::GdiplusStartup(&GDiplusToken, &GDiplusStartupInput, NULL);
		if (state != Gdiplus::Ok)
			return false;

		{
			// 主缓冲区和后缓冲区
			GCurrentBuffer		= new Gdiplus::Bitmap(SCREEN_WIDTH, SCREEN_HEIGHT, PixelFormat32bppRGB);
			GCurrentGraphics	= new Gdiplus::Graphics(GCurrentBuffer);

			GFont				= new Gdiplus::Font(L"Arial", 10);

			GBitmapData			= new Gdiplus::BitmapData;
			Gdiplus::Rect rect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
			GCurrentBuffer->LockBits(&rect,Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, GBitmapData);

			// pixels[row * bitmapData->Stride / 4 + col]
			GPixels = (UINT*)GBitmapData->Scan0;
			GCurrentBuffer->UnlockBits(GBitmapData);

			// 设置采样高质量
			GCurrentGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		}
#else
//
		GBufferedHDC = ::CreateCompatibleDC(nullptr);

		////////////////////
		BITMAPINFO info					= {0};    
		info.bmiHeader.biSize			= sizeof(info.bmiHeader);    
		info.bmiHeader.biWidth			= SCREEN_WIDTH;
		info.bmiHeader.biHeight			= -SCREEN_HEIGHT;    
		info.bmiHeader.biPlanes			= 1;    
		info.bmiHeader.biBitCount		= 32;    
		info.bmiHeader.biCompression	= BI_RGB;    
		info.bmiHeader.biSizeImage		= SCREEN_WIDTH * SCREEN_HEIGHT * 32 / 8;  

		// 创建一块内存纹理并获取其数据指针
		void* pBits = nullptr;
		GBufferedHandle = ::CreateDIBSection(GBufferedHDC, &info,
		        DIB_RGB_COLORS, &pBits, nullptr, 0);
		::SelectObject(GBufferedHDC, GBufferedHandle);

		//here: "dib.dsBm.bmBits" will points to the pixels of hdib.
		::GetObject(GBufferedHandle , sizeof(DIBSECTION), &GDIBSection);
		//?
		GData = (BYTE*)GDIBSection.dsBm.bmBits;
		//?
		GPitch = GDIBSection.dsBm.bmWidthBytes;

		// 设置刷新区域大小
		::SetRect(&GBufferSize, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

		//GPen = (HPEN)::GetStockObject(WHITE_PEN); 
		GPen = ::CreatePen(PS_SOLID, 1, RGB(255, 255, 255)); 
		::SelectObject(GBufferedHDC, GPen);

		GBgBrush= ::CreateSolidBrush(RGB(0, 0, 0));
		::SelectObject (GBufferedHDC, GBgBrush) ;

		// 设置字体
		auto pHfont = (HFONT)::GetStockObject(OEM_FIXED_FONT);
		::SelectObject(GBufferedHDC, pHfont);
		// 设置文字背景为透明色
		::SetBkMode(GBufferedHDC, TRANSPARENT); 
#endif
		//??
		GZBuffer = new EFloat[SCREEN_WIDTH * SCREEN_HEIGHT];
		memset(GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT);

		return true;
	}

	////--------------------------------------------------------------------------

	// 关闭绘图系统
	void EGraphics::shutdownGraphics()
	{
#ifdef	GRAPHICS_DGIPLAS
		{
			GCurrentBuffer		= 0;
			GCurrentGraphics	= 0;
			SafeDelete(GFont);
			SafeDelete(GCurrentGraphics);
			SafeDelete(GCurrentBuffer);
		}
		Gdiplus::GdiplusShutdown(GDiplusToken);
#else
		::DeleteObject(GPen);
		::DeleteObject(GBgBrush);
		::DeleteObject(GBufferedHandle);
		::DeleteDC(GBufferedHDC);
#endif
	}

	////--------------------------------------------------------------------------

	// 清空当前缓冲区, 并将其颜色设置为黑色
	void EGraphics::clearBuffer(const EColor &c)
	{
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentGraphics)
			GCurrentGraphics->Clear(Gdiplus::Color(c.a, c.r, c.g, c.b));
#else
		//HBRUSH bgBrush;
		//bgBrush = ::CreateSolidBrush(RGB(c.r, c.g, c.b));
		//::SelectObject (GBufferedHDC, bgBrush) ;

		::FillRect(GBufferedHDC, &GBufferSize, GBgBrush);
		// 重置深度缓存
		// 注 : 这里memset只能对Int类型的数组进行初始化, 所以这里直接使用了Int类型
		// 而没有使用float类型, 应该使用float
		::memset(GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT);
			
		//::DeleteObject(bgBrush);
#endif
	}

	////--------------------------------------------------------------------------

	// 在当前缓冲区内绘制一条线
	void EGraphics::drawLine(EInt x0, EInt y0, EInt x1, EInt y1, const EColor &c)
	{
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentGraphics)
		{
			Gdiplus::Pen pen(Gdiplus::Color(c.a, c.r, c.g, c.b));
			GCurrentGraphics->DrawLine(&pen, x0, y0, x1, y1);
		}
#else
		//HPEN hPen = ::CreatePen(PS_SOLID, 1, RGB(c.r, c.g, c.b)); 
		//::SelectObject(GBufferedHDC, hPen);

		::SelectObject(GBufferedHDC,GetStockObject(DC_PEN));
		::SetDCPenColor(GBufferedHDC, RGB(c.r, c.g, c.b));
		
		::MoveToEx(GBufferedHDC, x0, y0, nullptr);
		::LineTo(GBufferedHDC, x1, y1);
		
		//::DeleteObject(hPen);
#endif
	}

	void EGraphics::drawString(const EString &str, EInt x, EInt y, const EColor &c)
	{
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentGraphics)
		{
			Gdiplus::PointF origin(x, y);
			Gdiplus::SolidBrush blackBrush(Gdiplus::Color(255, c.r, c.g, c.b));

			GCurrentGraphics->DrawString(ToEWString(str).c_str(), -1, GFont,origin,&blackBrush);
		}
#else
//?
		::SetTextColor(GBufferedHDC, RGB(c.r, c.g, c.b));
		::TextOut(GBufferedHDC, x, y, str.c_str(), str.length()); 
#endif
	}

	void EGraphics::fillTriangle(EInt x0, EInt y0, EInt x1, EInt y1, EInt x2, EInt y2, const EColor &c)
	{
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentGraphics)
		{
			Gdiplus::Point points[3]; 
			points[0].X = x0, points[0].Y = y0;
			points[1].X = x1, points[1].Y = y1;
			points[2].X = x2, points[2].Y = y2;
			Gdiplus::SolidBrush blackBrush(Gdiplus::Color(255, c.r, c.g, c.b));

			GCurrentGraphics->FillPolygon(&blackBrush, points, 3);
		}
#else
#endif
	}

	void EGraphics::enableSmoothingMode(EBool enable)
	{
#ifdef	GRAPHICS_DGIPLAS
		if(enable)
		{
			GCurrentGraphics->SetSmoothingMode(Gdiplus::SmoothingModeHighQuality);
		}
		else
		{
			GCurrentGraphics->SetSmoothingMode(Gdiplus::SmoothingModeDefault);
		}
#else
#endif
	}

	////--------------------------------------------------------------------------

	EBool EGraphics::checkZ(EInt x, EInt y, EFloat z)
	{
		// 这里Z应该使用float类型来存储, 如果使用int类型, 那么会导致精度丢失
		// 产生错误的现象
		EInt index = y * SCREEN_WIDTH + x;
		EFloat divZ = 1.0f / z;
		// 这里是基于1/z做的比较
		if (GZBuffer[index] > divZ)
			return false;

		GZBuffer[index] = divZ;
		return true;
	}

	////--------------------------------------------------------------------------

	/*
	 *
	 */
	void EGraphics::setPixel(EInt x, EInt y, /*EFloat z, */const EColor &c)
	{
		// 这里本来应该计算z值,但是为了避免对Image像素的读取, 我将z检查分离了出来
		// 所以, 在调用setPixel之前应该先检测checkZ, 返回true在调用setPixel
		// ----------------------------------------------------------------
		// 这里Z应该使用float类型来存储, 如果使用int类型, 那么会导致精度丢失
		// 产生错误的现象
		//EInt index = y * SCREEN_WIDTH + x;
		//EFloat divZ = 1.0f / z;
		//// 这里是基于1/z做的比较
		//if (GZBuffer[index] > divZ)
		//	return;

		//GZBuffer[index] = divZ;

#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentBuffer)
		{
			//GCurrentBuffer->SetPixel(x, y, Gdiplus::Color(c.a, c.r, c.g, c.b));
			GPixels[y * GBitmapData->Stride / 4 + x] = c.ToInt();
		}
#else
		//SetPixel(GBufferedHDC, x, y, RGB(c.r, c.g, c.b));

		//each scan line in a bitmap has a width, a.k.a : pitch
		//int pitch = GDIBSection.dsBm.bmWidthBytes;
		//???
		BYTE* pSrcPix = GData + (GPitch * y);
		pSrcPix += x * 4;
		// blue green red alpha
		pSrcPix[0] = c.b;
		pSrcPix[1] = c.g;
		pSrcPix[2] = c.r;
		pSrcPix[3] = c.a;
#endif
	}

	////--------------------------------------------------------------------------

	EColor EGraphics::getPixel(EInt x, EInt y)
	{
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentBuffer)
		{
			//Gdiplus::Color c;
			//GCurrentBuffer->GetPixel(x, y, &c);
			//return EColor(c.GetR(), c.GetG(), c.GetB(), c.GetA());
			EInt color = GPixels[x * GBitmapData->Stride / 4 + y];
			return EColor(color);
		}

		return EColor();
#else
		//COLORREF color = ::GetPixel(GBufferedHDC, x, y);
		//return EColor(GetRValue(color), GetGValue(color), GetBValue(color));

		//int pitch = GDIBSection.dsBm.bmWidthBytes;
		//pointer that will advance in memory one pixel at a time...
		BYTE* pSrcPix = GData + (GPitch * y);
		pSrcPix += x * 4;
		return EColor(pSrcPix[2], pSrcPix[1], pSrcPix[0]);
#endif
	}

	////--------------------------------------------------------------------------

	// 将已经绘制好的缓冲区递交给Graphics在屏幕上绘制, 并将当前缓冲区设置为另一个缓冲区
	void EGraphics::fillBuffer(HDC hdc)
	{	
#ifdef	GRAPHICS_DGIPLAS
		if (GCurrentGraphics)
		{
			Gdiplus::Graphics graphics(hdc);
			graphics.DrawImage(GCurrentBuffer, 0, 0/*, SCREEN_WIDTH, SCREEN_HEIGHT*/);
		}
#else
		::BitBlt(hdc,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,GBufferedHDC,0,0,SRCCOPY);
#endif
	}

	//--------------------------------------------------------------------------
}